{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Design - Quantum Operations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Quantum computing resembles classical computing in some aspects, and is substantially different in other aspects. One of the key advantages of Qmod is that it captures the core concepts which are uniquely quantum in a simple and natural way. This begins with the quantum objects and variables described in the previous tutorial (see [Quantum Variables and Functions](./quantum_variables_and_functions.ipynb)) and continues with the meaningful native quantum operations.\n",
    "\n",
    "Simply put, quantum operations are functions of functions applied on quantum objects that are very common in quantum computing, hence receive a special place in the Qmod language. More accurately, these are built-in statements in the Qmod language. A statement is a building block of a programming language, such that programming languages are composed from statements. For example, `if` and `for` loops are common statements in programming languages like in Python.\n",
    "\n",
    "There are a few quantum operation statements in Qmod, and in the following we focus on arguably the most useful one - `control`. It applies a specified quantum function conditioned on some state (value) of a given quantum variable. Other quantum operations are `invert`, `power`, and `within_apply`, and one can see all the quantum operations [here](https://docs.classiq.io/latest/reference-manual/platform/qmod/language-reference/statements/control/).\n",
    "\n",
    "Let's understand it through a concrete example."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Concrete Example"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Our task is to first prepare a quantum number `x` in a superposition of all possible integers between $0$ to $15$, i.e. $|x\\rangle = \\frac{1}{\\sqrt{16}}(|0\\rangle + |1\\rangle + \\dots + |15\\rangle)$. Then to prepare another quantum number `y` that is in the state $|17\\rangle$ only if $|x\\rangle$ is in the state $|15\\rangle$, otherwise the state of `y` should be $|0\\rangle$. Mathematically, the task is to prepare the following state:\n",
    "$\\begin{equation}\n",
    "|x\\rangle|y\\rangle = \\frac{1}{\\sqrt{16}}(|0\\rangle|0\\rangle+|1\\rangle|0\\rangle+\\dots+|14\\rangle|0\\rangle+|15\\rangle|17\\rangle).\n",
    "\\end{equation}$\n",
    "\n",
    "How to approach this task? We have already seen that we can create a uniform superposition with the `hadamard_transform`. Then, conditioned on the value of $|x\\rangle$ being $|15\\rangle$, we will prepare the state of $|y\\rangle$ to be $|17\\rangle$ with the `inplace_prepare_int` function."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We shell start by creating our own function called `apply_control` that received two quantum numbers `x` and `y` that are already initialized, and conditioned on the values of `x` being $15$, it prepares the state of `y` to the integer value $17$ using the `inplace_prepare_int` function.\n",
    "\n",
    "<details>\n",
    "<summary> What's the difference between `inplace_prepare_int` and `prepare_int`? </summary>\n",
    "The function `prepare_int` initializes a specific quantum variable to a specific integer value. On the other hand, the function `inplace_prepare_int` receives an initialized quantum variable, and assumed its value is $0$, it prepares its state to the desired integer values. Note that for all the `prepare` functions there is the option to use also `inplace_prepare` in case you want to apply the function on an already initialized quantum variable.\n",
    "</details>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:28:30.355762Z",
     "iopub.status.busy": "2024-05-07T13:28:30.353034Z",
     "iopub.status.idle": "2024-05-07T13:28:33.004386Z",
     "shell.execute_reply": "2024-05-07T13:28:33.003581Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import QNum, control, inplace_prepare_int, qfunc\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def apply_control(x: QNum, y: QNum):\n",
    "    control(ctrl=(x == 15), operand=lambda: inplace_prepare_int(17, y))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the Python syntax, `control` is a Python function with two arguments. The `ctrl` argument is the condition for which the `operand` will be applied. The `operand` argument is the specific operation/function we want to apply given the condition is satisfied. The way to pass a function as an argument of another function is with the `lambda:` keyword. **So anytime you pass a function as argument in the Python SDK use the prefix `lambda:`**.\n",
    "\n",
    "<details>\n",
    "<summary> More on `lambda:`</summary>\n",
    "The `lambda:` keyword is used to define inline functions.\n",
    "</details>\n",
    "\n",
    "In the Native syntax, `control` is embedded within the Qmod language such that the condition is specified with the `()` parenthesis, and the operand that needs to be applied is specified within the scope of the `control`, i.e. within the `{}` parenthesis.\n",
    "\n",
    "Now for the `main` function. At the end, we want to evaluate the execution results of both `x` and `y` so both of them will be arguments of the `main` function. `x` is initialized with $4$ qubits since it needs to be in a superposition of $16 (=2^4)$ states, and `y` is initialized with $5$ qubits since this is the minimal number of qubits that is needed for representing the number $17$ ($ceiling(log_2(17))=5$)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:28:33.010205Z",
     "iopub.status.busy": "2024-05-07T13:28:33.008778Z",
     "iopub.status.idle": "2024-05-07T13:28:33.015225Z",
     "shell.execute_reply": "2024-05-07T13:28:33.014493Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import Output, allocate, hadamard_transform\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def main(x: Output[QNum], y: Output[QNum]):\n",
    "    allocate(4, x)\n",
    "    allocate(5, y)\n",
    "\n",
    "    hadamard_transform(x)\n",
    "    apply_control(x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After the initialization, `hadamard_transform` is applied on `x` in order to prepare the initial superposition, and then the `apply_control` function we defined earlier is applied on both `x` and `y`.\n",
    "\n",
    "That's it! \n",
    "The desired quantum program can be now synthesized and viewed:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:28:33.020439Z",
     "iopub.status.busy": "2024-05-07T13:28:33.019199Z",
     "iopub.status.idle": "2024-05-07T13:28:35.551346Z",
     "shell.execute_reply": "2024-05-07T13:28:35.550687Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/ce544494-280d-4c50-b991-66b86a27e617?version=0.41.0.dev39%2B79c8fd0855\n"
     ]
    }
   ],
   "source": [
    "from classiq import create_model, show, synthesize\n",
    "\n",
    "quantum_program = synthesize(create_model(main))\n",
    "show(quantum_program)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div  style=\"text-align:center;\">\n",
    "    <img src=\"https://docs.classiq.io/resources/quantum_operations_synthesize.gif\">\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And then the quantum program can be executed in order to receive the execution results:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:28:35.588715Z",
     "iopub.status.busy": "2024-05-07T13:28:35.588160Z",
     "iopub.status.idle": "2024-05-07T13:28:36.875830Z",
     "shell.execute_reply": "2024-05-07T13:28:36.875110Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[{'x': 13.0, 'y': 0.0}: 80, {'x': 14.0, 'y': 0.0}: 70, {'x': 8.0, 'y': 0.0}: 70, {'x': 15.0, 'y': 17.0}: 67, {'x': 5.0, 'y': 0.0}: 66, {'x': 12.0, 'y': 0.0}: 65, {'x': 4.0, 'y': 0.0}: 64, {'x': 6.0, 'y': 0.0}: 63, {'x': 1.0, 'y': 0.0}: 61, {'x': 7.0, 'y': 0.0}: 59, {'x': 3.0, 'y': 0.0}: 59, {'x': 10.0, 'y': 0.0}: 59, {'x': 0.0, 'y': 0.0}: 57, {'x': 2.0, 'y': 0.0}: 56, {'x': 11.0, 'y': 0.0}: 54, {'x': 9.0, 'y': 0.0}: 50]\n"
     ]
    }
   ],
   "source": [
    "from classiq import execute\n",
    "\n",
    "job = execute(quantum_program)\n",
    "results = job.result()[0].value.parsed_counts\n",
    "print(results)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div  style=\"text-align:center;\">\n",
    "    <img src=\"https://docs.classiq.io/resources/quantum_operations_execute.gif\">\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see that we receive $16$ possible values for `x` and `y`, and in all the pairs of values $y=0$ besides when $x=15$ as desired!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary - Quantum Operations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following summarizes the main takeaways from the example in terms of quantum operations:\n",
    "\n",
    "* Quantum operations receive both quantum objects and quantum functions as inputs, and they apply the quantum functions on the quantum objects according to the nature of the quantum operation. In the above example, the `control` operation applies the `inplace_prepare_int` function on the quantum variable `y` conditioned on the value of the quantum variable `x` being $15$.\n",
    "\n",
    "* In the Native syntax, the quantum operations are statements of the Qmod language and have a special syntax. In the above example, the syntax for `control` is `control(){;}` where the condition is given in the parentheses and the operation/function that is applied is within the curly brackets.\n",
    "\n",
    "* In the Python SDK, the quantum operations are written just like any other Python function. The arguments of the quantum operation that are functions by themselves must be passed with the `lambda:` keyword. In the above example, the `operand` argument of the `control` function is a function by itself (`inplace_prepare_int`), hence it is prefixed with `lambda:`.\n",
    "\n",
    "* Other quantum operations are: `power` - raising a unitary to some power, `invert` - applying the inverse of a unitary, `within_apply` - applying two unitaries $U$ and $V$ in the following way: $UVU^\\dagger$. See more detailed description of all the quantum operators [here](https://docs.classiq.io/latest/reference-manual/platform/qmod/language-reference/statements/power/).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T13:28:36.879641Z",
     "iopub.status.busy": "2024-05-07T13:28:36.879126Z",
     "iopub.status.idle": "2024-05-07T13:28:36.906724Z",
     "shell.execute_reply": "2024-05-07T13:28:36.905988Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import write_qmod\n",
    "\n",
    "write_qmod(create_model(main), \"quantum_operations\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.11.7 ('classiq_devolpment')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  },
  "vscode": {
   "interpreter": {
    "hash": "e992e515f6583afc67b46eeabcda0f30363069fab8b382c7517b274ba7a59477"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
